/*
 * Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-12-07     cmcc       the first version
 */
#include "ns_log.h"

#include <os_stddef.h>
#include <os_assert.h>

#include <sys/time.h>
#include <cJSON.h>


#include "ns_image_recog.h"

#include "../util/fileUtil.h"
#include "../util/httpUtil.h"

static int _ns_fire_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Fire_Detection* result);
static int _ns_mask_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Mask_Detection* result);
static int _ns_violation_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Violation_Detection* result);
static int _ns_object_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Object_detection* result);

static cJSON* _image_recog(char* baseUrl, char* token, char* key, char* contentBuffer, long contentSize, char* filePath);
//static cJSON* _image_recog_mf(char* baseUrl, char* token, char* contentBuffer, long contentSize, char* filePath);
static int _check_json(cJSON* json);

/**
 * Fire detection.
 *
 * @param token authorize token
 * @param contentBuffer content of image
 * @param contentSize length of content
 * @param result NS_Fire_Detection
 * @return the result of fire detection, OS_EOK if success, else -OS_ERROR
 */
int ns_fire_detection(char* token, char* contentBuffer, long contentSize, NS_Fire_Detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_fire_detection(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Fire detection by file.
 *
 * @param token authorize token
 * @param filePath file path of image
 * @param result NS_Fire_Detection
 * @return the result of fire detection, OS_EOK if success, else -OS_ERROR
 */
int ns_fire_detection_file(char* token, char* filePath, NS_Fire_Detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_fire_detection(token, OS_NULL, -1, filePath, result);
}

/**
 * Mask detection.
 *
 * @param token authorize token
 * @param contentBuffer content of image
 * @param contentSize length of content
 * @param result NS_Mask_Detection
 * @return the result of mask detection, OS_EOK if success, else -OS_ERROR
 */
int ns_mask_detection(char* token, char* contentBuffer, long contentSize, NS_Mask_Detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_mask_detection(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Mask detection by file.
 *
 * @param token authorize token
 * @param filePath file path of image
 * @param result NS_Mask_Detection
 * @return the result of mask detection, OS_EOK if success, else -OS_ERROR
 */
int ns_mask_detection_file(char* token, char* filePath, NS_Mask_Detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_mask_detection(token, OS_NULL, -1, filePath, result);
}

/**
 * Image violation detection.
 *
 * @param token authorize token
 * @param contentBuffer content of image
 * @param contentSize length of content
 * @param result NS_Violation_Detection
 * @return the result of violation detection, OS_EOK if success, else -OS_ERROR
 */
int ns_violation_detection(char* token, char* contentBuffer, long contentSize, NS_Violation_Detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_violation_detection(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Image violation detection by file.
 *
 * @param token authorize token
 * @param filePath file path of image
 * @param result NS_Violation_Detection
 * @return the result of violation detection, OS_EOK if success, else -OS_ERROR
 */
int ns_violation_detection_file(char* token, char* filePath, NS_Violation_Detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_violation_detection(token, OS_NULL, -1, filePath, result);
}

/**
 * Image object detection.
 *
 * @param token authorize token
 * @param contentBuffer content of image
 * @param contentSize length of content
 * @param result NS_Object_detection
 * @return the result of object detection, OS_EOK if success, else -OS_ERROR
 */
int ns_object_detection(char* token, char* contentBuffer, long contentSize, NS_Object_detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_object_detection(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Image object detection by file.
 *
 * @param token authorize token
 * @param filePath file path of image
 * @param result NS_Object_detection
 * @return the result of object detection, OS_EOK if success, else -OS_ERROR
 */
int ns_object_detection_file(char* token, char* filePath, NS_Object_detection* result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_object_detection(token, OS_NULL, -1, filePath, result);
}


static int _ns_fire_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Fire_Detection* result)
{
    cJSON* json = _image_recog(URL_FIRE_DETECT_RECOG, token, "image", contentBuffer, contentSize, filePath);
    if(_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON* data = cJSON_GetObjectItem(json, "data");
    result->fire = atof(cJSON_GetObjectItem(data, "fire")->valuestring);
    result->nofire = atof(cJSON_GetObjectItem(data, "nofire")->valuestring);

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_mask_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Mask_Detection* result)
{
    cJSON* json = _image_recog(URL_MASK_DETECT_RECOG, token, "img", contentBuffer, contentSize, filePath);
    if(_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    char* HAVE_MASK = "have_mask";
    char* NO_MASK = "no_mask";
    char* data = cJSON_GetObjectItem(json, "data")->valuestring;

    int len = strlen(data);
    int total_sep = 0;
    for(int i=0; i<len; i++)
    {
        if(*(data+i) == ',')
        {
            total_sep ++;
        }
    }
    total_sep++; //end

    if(total_sep % 5 != 0)
    {
        LOG_EXT_E("data parse error");
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    int object_count = total_sep/5;
    int* object_have_mask = os_malloc(sizeof(int) * object_count);
    char** object_coordinate = os_malloc(sizeof(char*) * object_count);

    int start_pos = 0, sep_count = 0, object_index = 0;
    for(int i=0; i<= len; i++)
    {
        if(*(data+i) == ',')
        {
            sep_count ++;
        }
        if(sep_count == 5 || i == len)
        {
            *(data+i) = '\0';
            if(strncmp(HAVE_MASK, data+start_pos, strlen(HAVE_MASK)) == 0)
            {
                *(object_have_mask + object_index) = 1;
                *(object_coordinate + object_index) = strdup(data+start_pos+strlen(HAVE_MASK)+1);
            }
            else if(strncmp(NO_MASK, data+start_pos, strlen(NO_MASK)) == 0)
            {
                *(object_have_mask+object_index) = 0;
                *(object_coordinate + object_index) = strdup(data+start_pos+strlen(NO_MASK)+1);
            }
            else
            {
                LOG_EXT_E("data format error");
                cJSON_Delete(json);
                os_free(object_have_mask);
                os_free(object_coordinate);
                return -OS_ERROR;
            }
            start_pos = i + 1;
            sep_count = 0;
            object_index++;
        }
    }

    result->objectCount = object_count;
    result->haveMask = object_have_mask;
    result->coordinates = object_coordinate;

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_violation_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Violation_Detection* result)
{
    cJSON* json = _image_recog(URL_VIOLATION_DETECT_RECOG, token, "image", contentBuffer, contentSize, filePath);
    if(_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON* data = cJSON_GetObjectItem(json, "data");
    result->noviolation = cJSON_GetObjectItem(data, "detectResult")->valueint;

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_object_detection(char* token, char* contentBuffer, long contentSize, char* filePath, NS_Object_detection* result)
{
    cJSON* json = _image_recog(URL_OBJECT_DETECT_RECOG, token, "image", contentBuffer, contentSize, filePath);
    if(_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON* data = cJSON_GetObjectItem(json, "data");
    int object_count = cJSON_GetArraySize(data);
    if(object_count == 0)
    {
        result->objectCount = 0;
        result->items = OS_NULL;
        cJSON_Delete(json);
        return OS_EOK;
    }

    NS_Object_detection_item* object_item_array = os_malloc(sizeof(NS_Object_detection_item) * object_count);

    cJSON* object_element_json = OS_NULL;
    int index = 0;
    cJSON_ArrayForEach(object_element_json, data)
    {
        (object_item_array + index)->objectLabel = cJSON_GetObjectItem(object_element_json, "label")->valueint;
        (object_item_array + index)->score = (float)(cJSON_GetObjectItem(object_element_json, "score")->valuedouble);
        (object_item_array + index)->xmax = (float)(cJSON_GetObjectItem(object_element_json, "xmax")->valuedouble);
        (object_item_array + index)->xmin = (float)(cJSON_GetObjectItem(object_element_json, "xmin")->valuedouble);
        (object_item_array + index)->ymax = (float)(cJSON_GetObjectItem(object_element_json, "ymax")->valuedouble);
        (object_item_array + index)->ymin = (float)(cJSON_GetObjectItem(object_element_json, "ymin")->valuedouble);
        index++;
    }

    result->objectCount = object_count;
    result->items = object_item_array;

    cJSON_Delete(json);
    return OS_EOK;
}

static cJSON* _image_recog(char* baseUrl, char* token, char* key, char* contentBuffer, long contentSize, char* filePath)
{
    //build url
    char url[200] = {0};
    strcat(url,baseUrl);
    strcat(url, "access_token=");
    strcat(url, token);
    LOG_EXT_D("url:%s", url);

    //send http request and get JSON response
    if(filePath != OS_NULL)
    {
        return http_post_file_base64(url, key, filePath);
    }

    return http_post_content_base64(url, key, contentBuffer, contentSize);
}

/*
static cJSON* _image_recog_mf(char* baseUrl, char* token, char* contentBuffer, long contentSize, char* filePath)
{
    //build url
    char url[200] = {0};
    strcat(url,baseUrl);
    strcat(url, "access_token=");
    strcat(url, token);
    LOG_EXT_D("url:%s", url);

    char* argName[1] = {0};
    argName[0] = "name";

    char* argValue[2] = {0};
    argValue[0] = "ad";

    //send http request and get JSON response
    if(filePath != OS_NULL)
    {
        return http_post_file_multipart_form(url, filePath, argName, argValue, 1);
    }

    char fileName[30] = {0};
    struct timeval tv;
    gettimeofday(&tv, OS_NULL);
    sprintf(fileName, "%s_%ld_%d.jpeg", "im", (long)tv.tv_sec, (int)(contentSize&0xFF));
    return http_post_content_multipart_form(url, contentBuffer, contentSize, OS_NULL, argName, argValue, 1);
}
*/


static int _check_json(cJSON* json)
{
    if (json == OS_NULL) {
        return -OS_ERROR;
    }

    cJSON* code = cJSON_GetObjectItem(json, "code");
    if(code->valueint != 200)
    {
        LOG_EXT_E("invalid response, code:%d, msg:%s", code->valueint, (cJSON_GetObjectItem(json, "msg"))->valuestring);
        return -OS_ERROR;
    }

    cJSON* data = cJSON_GetObjectItem(json, "data");
    if (data == OS_NULL)
    {
        LOG_EXT_E("no data elements");
        return -OS_ERROR;
    }

    return OS_EOK;
}
